//
// This software is now distributed according to
// the Lesser Gnu Public License. Please see
// http://www.gnu.org/copyleft/lesser.txt for
// the details.
// -- Happy Computing!
//
package com.stevesoft.pat;

import java.util.Hashtable;

/**
 * A special case of Multi, implemented when minChars().equals(maxChars()), and
 * some other conditions spelled out in RegOpt.safe4fm "Safe for FastMulti." It
 * avoids stack growth problems as well as being slightly faster.
 */
class FastMulti extends PatternSub {

	patInt fewestMatches, mostMatches;

	public patInt minChars() {
		return sub.countMinChars().mul(fewestMatches);
	}

	public patInt maxChars() {
		return sub.countMaxChars().mul(mostMatches);
	}

	public boolean matchFewest = false;

	FastMulti(patInt a, patInt b, Pattern p) throws RegSyntax {
		if (p == null)
			RegSyntaxError.endItAll("Null length pattern " + "followed by *, +, or other Multi.");
		fewestMatches = a;
		mostMatches = b;
		sub = p;
		step = p.countMinChars().intValue();
		sub.setParent(null);
	}

	public String toString() {
		return sub.toString() + "{" + fewestMatches + "," + mostMatches + "}" + (matchFewest ? "?" : "")
				+ "(?# <= fast multi)" + nextString();
	}

	int step = -1;

	public int matchInternal(int pos, Pthings pt) {
		int m = -1;
		int i = pos;
		int endstr = pt.src.length() - step;
		patInt matches = new patInt(0);
		if (matchFewest) {
			if (fewestMatches.lessEq(matches)) {
				int ii = nextMatch(i, pt);
				if (ii >= 0)
					return ii;
			}
			while (i >= 0 && i <= endstr) {
				i = sub.matchInternal(i, pt);
				if (i >= 0) {
					matches.inc();
					if (fewestMatches.lessEq(matches)) {
						int ii = nextMatch(i, pt);
						if (ii >= 0)
							return ii;
					}
					if (matches.equals(mostMatches))
						return -1;
				}
			}
			return -1;
		}
		int nMatches = 0;
		while (fewestMatches.intValue() > nMatches) {
			i = sub.matchInternal(i, pt);
			if (i >= 0)
				nMatches++;
			else
				return -1;
		}
		m = i;
		if (mostMatches.finite()) {
			while (nMatches < mostMatches.intValue()) {
				i = sub.matchInternal(i, pt);
				if (i >= 0) {
					m = i;
					nMatches++;
				}
				else
					break;
			}
		}
		else {
			while (true) {
				i = sub.matchInternal(i, pt);
				if (i >= 0) {
					m = i;
					nMatches++;
				}
				else
					break;
			}
		}
		while (m >= pos) {
			int r = nextMatch(m, pt);
			if (r >= 0)
				return r;
			m -= step;
			nMatches--;
			if (nMatches < fewestMatches.intValue())
				return -1;
		}
		return -1;
	}

	public Pattern clone1(Hashtable h) {
		try {
			FastMulti fm = new FastMulti(fewestMatches, mostMatches, sub.clone(h));
			fm.matchFewest = matchFewest;
			return fm;
		} catch (RegSyntax rs) {
			return null;
		}
	}
}
